"""
Naming convention utilities for C++ code generation.

This module provides utilities for converting C# naming conventions to C++ style:
- PascalCase to snake_case
- camelCase to snake_case
- Handling C++ keyword conflicts
- Private member prefixes
"""

from __future__ import annotations

import re


class NamingConvention:
    """
    Utilities for converting C# names to C++ naming conventions.

    Supports configurable styles and handles keyword conflicts.
    """

    # C++ keywords that need to be avoided
    CPP_KEYWORDS = {
        "alignas",
        "alignof",
        "and",
        "and_eq",
        "asm",
        "auto",
        "bitand",
        "bitor",
        "bool",
        "break",
        "case",
        "catch",
        "char",
        "char8_t",
        "char16_t",
        "char32_t",
        "class",
        "compl",
        "concept",
        "const",
        "consteval",
        "constexpr",
        "constinit",
        "const_cast",
        "continue",
        "co_await",
        "co_return",
        "co_yield",
        "decltype",
        "default",
        "delete",
        "do",
        "double",
        "dynamic_cast",
        "else",
        "enum",
        "explicit",
        "export",
        "extern",
        "false",
        "float",
        "for",
        "friend",
        "goto",
        "if",
        "inline",
        "int",
        "long",
        "mutable",
        "namespace",
        "new",
        "noexcept",
        "not",
        "not_eq",
        "nullptr",
        "operator",
        "or",
        "or_eq",
        "private",
        "protected",
        "public",
        "register",
        "reinterpret_cast",
        "requires",
        "return",
        "short",
        "signed",
        "sizeof",
        "static",
        "static_assert",
        "static_cast",
        "struct",
        "switch",
        "template",
        "this",
        "thread_local",
        "throw",
        "true",
        "try",
        "typedef",
        "typeid",
        "typename",
        "union",
        "unsigned",
        "using",
        "virtual",
        "void",
        "volatile",
        "wchar_t",
        "while",
        "xor",
        "xor_eq",
    }

    def __init__(
        self,
        class_style: str = "PascalCase",
        method_style: str = "snake_case",
        field_style: str = "snake_case",
        parameter_style: str = "snake_case",
        private_prefix: str = "m_",
        constant_style: str = "UPPER_CASE",
    ):
        """
        Initialize naming convention.

        Args:
            class_style: Style for class names ("PascalCase" or "snake_case")
            method_style: Style for method names ("PascalCase", "camelCase", or "snake_case")
            field_style: Style for field names ("snake_case" or "camelCase")
            parameter_style: Style for parameter names ("snake_case" or "camelCase")
            private_prefix: Prefix for private members (e.g., "m_", "_", "")
            constant_style: Style for constants ("UPPER_CASE" or "snake_case")
        """
        self.class_style = class_style
        self.method_style = method_style
        self.field_style = field_style
        self.parameter_style = parameter_style
        self.private_prefix = private_prefix
        self.constant_style = constant_style

    def convert_class_name(self, csharp_name: str) -> str:
        """
        Convert C# class name to C++ style.

        Args:
            csharp_name: C# class name (usually PascalCase)

        Returns:
            C++ class name

        Examples:
            >>> nc = NamingConvention(class_style="PascalCase")
            >>> nc.convert_class_name("UserService")
            "UserService"

            >>> nc = NamingConvention(class_style="snake_case")
            >>> nc.convert_class_name("UserService")
            "user_service"
        """
        if self.class_style == "PascalCase":
            return csharp_name
        elif self.class_style == "snake_case":
            return self.to_snake_case(csharp_name)
        else:
            return csharp_name

    def convert_method_name(self, csharp_name: str) -> str:
        """
        Convert C# method name to C++ style.

        Args:
            csharp_name: C# method name (usually PascalCase)

        Returns:
            C++ method name

        Examples:
            >>> nc = NamingConvention(method_style="snake_case")
            >>> nc.convert_method_name("GetUserById")
            "get_user_by_id"
        """
        if self.method_style == "snake_case":
            result = self.to_snake_case(csharp_name)
        elif self.method_style == "camelCase":
            result = self.to_camel_case(csharp_name)
        elif self.method_style == "PascalCase":
            result = csharp_name
        else:
            result = csharp_name

        return self.avoid_keyword(result)

    def convert_field_name(self, csharp_name: str, is_private: bool = False) -> str:
        """
        Convert C# field name to C++ style.

        Args:
            csharp_name: C# field name (usually camelCase or _camelCase)
            is_private: Whether the field is private

        Returns:
            C++ field name with appropriate prefix

        Examples:
            >>> nc = NamingConvention(field_style="snake_case", private_prefix="m_")
            >>> nc.convert_field_name("userId", is_private=True)
            "m_user_id"
        """
        # Remove leading underscore if present (C# convention)
        name = csharp_name.lstrip("_")

        if self.field_style == "snake_case":
            result = self.to_snake_case(name)
        elif self.field_style == "camelCase":
            result = self.to_camel_case(name)
        else:
            result = name

        # Add private prefix if needed
        if is_private and self.private_prefix:
            result = f"{self.private_prefix}{result}"

        return self.avoid_keyword(result)

    def convert_parameter_name(self, csharp_name: str) -> str:
        """
        Convert C# parameter name to C++ style.

        Args:
            csharp_name: C# parameter name (usually camelCase)

        Returns:
            C++ parameter name

        Examples:
            >>> nc = NamingConvention(parameter_style="snake_case")
            >>> nc.convert_parameter_name("userId")
            "user_id"
        """
        if self.parameter_style == "snake_case":
            result = self.to_snake_case(csharp_name)
        elif self.parameter_style == "camelCase":
            result = self.to_camel_case(csharp_name)
        else:
            result = csharp_name

        return self.avoid_keyword(result)

    def convert_constant_name(self, csharp_name: str) -> str:
        """
        Convert C# constant name to C++ style.

        Args:
            csharp_name: C# constant name (usually UPPER_CASE or PascalCase)

        Returns:
            C++ constant name

        Examples:
            >>> nc = NamingConvention(constant_style="UPPER_CASE")
            >>> nc.convert_constant_name("MaxConnections")
            "MAX_CONNECTIONS"
        """
        if self.constant_style == "UPPER_CASE":
            return self.to_upper_case(csharp_name)
        elif self.constant_style == "snake_case":
            return self.to_snake_case(csharp_name)
        else:
            return csharp_name

    def to_snake_case(self, name: str) -> str:
        """
        Convert name to snake_case.

        Args:
            name: Name in any case

        Returns:
            Name in snake_case

        Examples:
            >>> NamingConvention().to_snake_case("PascalCase")
            "pascal_case"

            >>> NamingConvention().to_snake_case("camelCase")
            "camel_case"

            >>> NamingConvention().to_snake_case("HTTPServer")
            "http_server"
        """
        # Handle already snake_case
        if "_" in name and name.islower():
            return name

        # Insert underscore before uppercase letters following lowercase
        s1 = re.sub("(.)([A-Z][a-z]+)", r"\1_\2", name)

        # Insert underscore before uppercase letters in acronyms
        s2 = re.sub("([a-z0-9])([A-Z])", r"\1_\2", s1)

        # Handle numbers
        s3 = re.sub("([a-zA-Z])([0-9])", r"\1_\2", s2)

        return s3.lower()

    def to_camel_case(self, name: str) -> str:
        """
        Convert name to camelCase.

        Args:
            name: Name in any case

        Returns:
            Name in camelCase

        Examples:
            >>> NamingConvention().to_camel_case("PascalCase")
            "pascalCase"

            >>> NamingConvention().to_camel_case("snake_case")
            "snakeCase"
        """
        if not name:
            return name

        # If already camelCase, return as is
        if name[0].islower() and "_" not in name:
            return name

        # If snake_case, convert to camelCase
        if "_" in name:
            parts = name.split("_")
            return parts[0].lower() + "".join(p.capitalize() for p in parts[1:])

        # If PascalCase, lowercase first letter
        return name[0].lower() + name[1:]

    def to_upper_case(self, name: str) -> str:
        """
        Convert name to UPPER_CASE.

        Args:
            name: Name in any case

        Returns:
            Name in UPPER_CASE

        Examples:
            >>> NamingConvention().to_upper_case("PascalCase")
            "PASCAL_CASE"

            >>> NamingConvention().to_upper_case("camelCase")
            "CAMEL_CASE"
        """
        snake = self.to_snake_case(name)
        return snake.upper()

    def avoid_keyword(self, name: str) -> str:
        """
        Avoid C++ keyword conflicts.

        Args:
            name: Proposed name

        Returns:
            Name with suffix if it's a keyword

        Examples:
            >>> NamingConvention().avoid_keyword("class")
            "class_"

            >>> NamingConvention().avoid_keyword("user_id")
            "user_id"
        """
        if name in self.CPP_KEYWORDS:
            return f"{name}_"
        return name

    def get_header_guard(self, namespace: str, class_name: str) -> str:
        """
        Generate header guard name.

        Args:
            namespace: C++ namespace (e.g., "my_app::services")
            class_name: C++ class name

        Returns:
            Header guard name (e.g., "MY_APP_SERVICES_USER_SERVICE_HPP_")

        Examples:
            >>> nc = NamingConvention()
            >>> nc.get_header_guard("my_app::services", "UserService")
            "MY_APP_SERVICES_USER_SERVICE_HPP_"
        """
        parts = []

        # Add namespace parts
        if namespace:
            parts.extend(namespace.split("::"))

        # Add class name
        parts.append(class_name)

        # Add .hpp suffix
        parts.append("HPP")

        # Join with underscores and convert to uppercase
        guard = "_".join(parts).upper()

        # Replace any remaining special characters
        guard = re.sub(r"[^A-Z0-9_]", "_", guard)

        # Ensure it ends with underscore
        if not guard.endswith("_"):
            guard += "_"

        return guard

    def get_include_name(self, namespace: str, class_name: str) -> str:
        """
        Generate include file name.

        Args:
            namespace: C++ namespace (e.g., "my_app::services")
            class_name: C++ class name

        Returns:
            Include file name (e.g., "my_app/services/user_service.hpp")

        Examples:
            >>> nc = NamingConvention()
            >>> nc.get_include_name("my_app::services", "UserService")
            "my_app/services/user_service.hpp"
        """
        parts = []

        # Add namespace parts
        if namespace:
            parts.extend(namespace.split("::"))

        # Add class name (convert to snake_case if needed)
        class_name_lower = self.to_snake_case(class_name)
        parts.append(class_name_lower)

        # Join with slashes and add .hpp extension
        return "/".join(parts) + ".hpp"
